﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DungeonCraft.Exceptions;
using DungeonCraft.DataModel.Factories;
using DungeonCraft.DataModel.Tools;

namespace DungeonCraft.DataModel
{
    public class Player
    {
        public enum Directions
        {
            Up,
            Down,
            Right,
            Left
        }

        #region Fields
        private string name;
        private int id;
        private Interfaces.ILevel level;
        private Interfaces.ILocation location;
        private string sprite;
        private Directions direction;
        private bool jumping;
        private int speed;
        private int height;
        private int width;
        private bool OnCooldown;
        #endregion

        #region Properties
        public int Speed
        {
            get
            {
                return speed;
            }
            set
            {
                speed = value;
            }
        }

        public string Name
        {
            get
            {
                if (string.IsNullOrEmpty(name))
                    throw new Exception("Player name must be set");
                return this.name;
            }
            set
            {
                int testNumeric;
                if (!int.TryParse(value, out testNumeric))
                    this.name = value;
                else
                    throw new ArgumentException("Name can't be a number");
            }
        }

        public int Id
        {
            get
            {
                if (id < 0)
                    throw new Exception("Player id must be set");
                return this.id;
            }
            set
            {
                if (value > -1)
                    this.id = value;
                else
                    throw new ArgumentException("Id must be a positive integer");
            }
        }

        public Interfaces.ILocation Location
        {
            get { return location; }

            set
            {
                if (level.LocationIsAllowed(value, this))
                    location = value;
                else
                    throw new LocationException("Location: '" + value.ToString() + "' is not allow for player: '" + Name + "(" + Id + ")'");
            }
        }

        public Interfaces.ILevel Level
        {
            get
            {
                return level;
            }
            set
            {
                level = value;
            }
        }

        public string Sprite
        {
            get
            {
                return sprite;
            }
            set
            {
                sprite = value;
            }
        }

        public bool Jumping
        {
            get
            {
                return jumping;
            }

            set
            {
                jumping = value;
            }
        }

        public int Height
        {
            get
            {
                return height;
            }
            set
            {
                height = value;
            }
        }

        public int Width
        {
            get
            {
                return width;
            }
            set
            {
                width = value;
            }
        }

        public Directions Direction { get { return direction; } set { direction = value; } }
        #endregion

        public Player()
        {
            // TODO: Complete member initialization
            this.direction = Directions.Down;
            this.speed = 1;
            height = 15;
            width = 10;
        }

        public Player(Interfaces.ILevel iLevel)
            : this()
        {
            // TODO: Complete member initialization
            this.level = iLevel;
        }

        public Player(string name, int id, Interfaces.ILevel level)
            : this(level)
        {
            // TODO: Complete member initialization
            this.Name = name;
            this.Id = id;
        }

        public Player(string name, int id, Interfaces.ILevel level, Interfaces.ILocation location)
            : this(name, id, level)
        {
            // TODO: Complete member initialization
            this.location = location;
        }

        public Player(string name, int id, Interfaces.ILevel level, Interfaces.ILocation location, string sprite)
            : this(name, id, level, location)
        {
            this.sprite = sprite;
        }

        public void StartCooldown(CallbackTimer timer)
        {
            this.OnCooldown = true;
            this.CoolDownTimer = timer;
            timer.EndCallBack = EndCooldown;
            timer.Start();
        }

        public void EndCooldown()
        {
            OnCooldown = false;
            this.CoolDownTimer = null;
        }


        public void StartJumpCooldown(CallbackTimer callbackTimer)
        {
            this.JumpCooldownTimer = callbackTimer;
            callbackTimer.EndCallBack = EndJumpCooldown;
            callbackTimer.Start();
        }

        public void EndJumpCooldown()
        {
            this.JumpCooldownTimer = null;
        }
        
        public void Move(int x, int y)
        {
            location.SetCoordinates(location.X + x, location.Y + y);
        }
        
        public bool canWalk()
        {
            return !jumping;
        }

        public bool canJump()
        {
            return !jumping && JumpCooldownTimer == null;
        }

        public bool canUseAbility()
        {
            return !jumping && !OnCooldown && CurrentAbility == null;
        }

        #region Obsolete

        [Obsolete("Use movement Abilities with guards instead")]
        public void WalkRight()
        {
            if (!canWalk())
                return;
            speed = 1;
            MoveRight();
        }

        [Obsolete("Use movement Abilities with guards instead")]
        public void WalkLeft()
        {
            if (!canWalk())
                return;
            speed = 1;
            MoveLeft();
        }

        [Obsolete("Use movement Abilities with guards instead")]
        public void WalkDown()
        {
            if (!canWalk())
                return;
            speed = 1;
            MoveDown();
        }

        [Obsolete("Use movement Abilities with guards instead")]
        public void WalkUp()
        {
            if (!canWalk())
                return;
            speed = 1;
            MoveUp();
        }

        [Obsolete("Use Jump ability and Guard")]
        public void Jump(Interfaces.ICallbackTimer timer)
        {
            if (jumping)
                return;
            switch (direction)
            {
                case Directions.Up:
                    if (!level.LocationIsAllowed(location.X, location.Y - (2 * Block.height) - 1, this))
                        return;
                    timer.TickCallBack = this.MoveUp;
                    break;
                case Directions.Down:
                    if (!level.LocationIsAllowed(location.X, location.Y + (2 * Block.height) + 1, this))
                        return;
                    timer.TickCallBack = this.MoveDown;
                    break;
                case Directions.Left:
                    if (!level.LocationIsAllowed(location.X - (2 * Block.width), location.Y - 1, this))
                        return;
                    timer.TickCallBack = this.MoveLeft;
                    break;
                case Directions.Right:
                    if (!level.LocationIsAllowed(location.X + (2 * Block.width), location.Y + 1, this))
                        return;
                    timer.TickCallBack = this.MoveRight;
                    break;
            }
            timer.EndCallBack = this.Land;
            jumping = true;
            speed = 2;
            timer.tickTime = 10;
            timer.NumberOfTicks = 15;
            timer.Start();
        }

        [Obsolete("Use Jump ability and Guard")]
        public void Land()
        {
            jumping = false;
        }

        [Obsolete("Use movement Abilities with guards instead")]
        public void MoveUp()
        {
            Move(0, -speed);
            direction = Directions.Up;
        }
        [Obsolete("Use movement Abilities with guards instead")]
        public void MoveDown()
        {
            Move(0, speed);
            direction = Directions.Down;
        }
        [Obsolete("Use movement Abilities with guards instead")]
        public void MoveLeft()
        {
            Move(-speed, 0);
            direction = Directions.Left;
        }
        [Obsolete("Use movement Abilities with guards instead")]
        public void MoveRight()
        {
            Move(speed, 0);
            direction = Directions.Right;
        }
        #endregion


        public int Mana { get; set; }

        public Abilities.MagicMissile CurrentAbility { get; set; }

        public CallbackTimer CoolDownTimer { get; set; }




        public CallbackTimer JumpCooldownTimer { get; set; }

        public void Kill()
        {
            Dead = true;
        }

        public bool Dead { get; set; }
    }
}
